/**
 * @version 1.0.2
 * @param{object} options
 * @constructor
 */
function JStyleSheet(options) {
  this.sheet = [];
  this.cssText = [];
  if (isObject(options)) {
    for (var item in options) {
      this.sheet.push(parseStyle(item, options[item]))
    }
  } else {
    console.error('Invalid StyleSheet in constructor')
  }
}

JStyleSheet.prototype = {
  toJSON: function () {
    return JSON.stringify(this.sheet)
  },
  compile: function () {
    for (var i = 0; i < this.sheet.length; i++) {
      this.cssText.push(compile(this.sheet[i]))
    }
    return this
  },
  append: function () {
    var result = [];
    var styleEl = document.createElement('style');
    for (var i = 0; i < this.cssText.length; i++) {
      result = result.concat(this.cssText[i])
    }
    styleEl.type = 'text/css';
    styleEl.textContent = result.join('\n');
    document.getElementsByTagName('head')[0].appendChild(styleEl)
  }
}

JStyleSheet.keyframes = function (options) {
  this.keyframes = options;
}

JStyleSheet.keyframes.prototype = {
  compile: function () {
    this.cssText = parseKeyFrames(this.keyframes);
    return this
  },
  append: function () {
    var styleEl = document.createElement('style');
    styleEl.type = 'text/css';
    styleEl.textContent = this.cssText.join('\n');
    document.getElementsByTagName('head')[0].appendChild(styleEl)
  }
}

JStyleSheet.keyframes.kernel = ['-webkit-', '-moz-', '-ms-', '-o-']

function parseKeyFrames (options) {
  var keyframes = {};
  for (var i in options) {
    keyframes[i] = testFrames(options[i])
  } return transformFrames(keyframes)
}

function testFrames (frames) {
  var result = [];
  for (var i in frames) {
    if (/^(\d{1,3}%$|from|to)/.test(i)) {
      result.push(i + ' { \n' + objectToArray(frames[i]).join('; \n') + '\n }')
    }
  } return result
}

function objectToArray (obj) {
  var array = [];
  for (var i in obj) {
    array.push(i + ': ' + obj[i])
  } return array
}

function transformFrames (frames) {
  var cssRule = [];
  var kernel = JStyleSheet.keyframes.kernel;
  for (var i in frames) {
    cssRule.push('@keyframes' + ' ' + i + ' { \n ' + frames[i].join('\n') + '\n }')
    for (var k = 0; k < kernel.length; k++) {
      cssRule.push('@' + kernel[k] + 'keyframes' + ' ' + i + ' { \n' + frames[i].join('\n') + ' \n }')
    }
  } return cssRule
}

function isObject (v) {
  return !!v && typeof v === 'object'
}

function toLowerCase (v) {
  var reg = /[A-Z]/g;
  if (!!v.match(reg)) {
    var match = v.match(reg).join('').toLowerCase().split('');
    var words = v.split(reg);
    var result  = [];
    for (var i = 0; i < words.length; i++) {
      if (i !== 0) {
        result.push(match[i - 1] + words[i])
      } else {
        result.push(words[i])
      }
    }
    return result.join('-')
  } else {
    return v
  }
}

function isEmptyObject (v) {
  for (var item in v) {
    return false
  } return true
}

function compile (sheet) {
  var result = [];
  for (var item in sheet) {
    var current = sheet[item];
    if (item === 'property') {
      if (current.length) {
        result.push(sheet.selector + ' { \n' + current.join('; \n') + ' \n}')
      }
    } else if (item === 'children') {
      for (var i = 0; i < current.length; i++) {
        var $item = current[i];
        if (/^&\.[\w-\W]+/.test($item.selector)) {
          $item.selector = sheet.selector + '' + $item.selector.replace(/^&/, '')
        } else if (/^&(:)(nth-child\([1-9]\)|first-child|last-child|not\([\w-\W]+\))/.test($item.selector)) {
          $item.selector = sheet.selector + ':' + $item.selector.replace(/^&:/, '')
        } else if (/^&(::)[\w-\W]+/.test($item.selector)) {
          $item.selector = sheet.selector + '::' + $item.selector.replace(/^&::/, '')
        } else {
          $item.selector = sheet.selector + ' ' + $item.selector
        }
        result = result.concat(compile($item))
      }
    } else if (item === 'pseudo' && !isEmptyObject(current)) {
      for (var $$item in current) {
        current[$$item].selector = sheet.selector + ':' + $$item;
        result = result.concat(compile(current[$$item]))
      }
    }
  } return result
}

var pseudoKey = [
  'hover',
  'link',
  'visited',
  'after',
  'before',
  'lang',
  'checked',
  'disabled',
  'enabled',
  'invalid',
  'root',
  'target',
  'focus',
  'blur',
  'active'
];

function parseStyle(selector, style) {
  var cssList = {
    property: [],
    pseudo: {},
    children: [],
    selector: selector
  };
  for(var name in style) {
    var item = style[name];
    switch (typeof item) {
      case 'string':
        // ::after or ::before
        var value = name === 'content'
          ? JSON.stringify(item)
          : item;
        cssList.property.push(toLowerCase(name) +': '+ value)
        break;
      case 'number':
        cssList.property.push(toLowerCase(name) +': '+ item)
        break;
      case 'object':
        var pureKey = name.replace(/^\$/, '');
        if (pseudoKey.indexOf(pureKey) !== -1) {
          cssList.pseudo[pureKey] = parseStyle(selector, item)
        } else {
          cssList.children.push(parseStyle(name, item))
        }
        break;
      default:
        console.warn('Invalid css property or selector.')
        break
    }
  } return cssList
}
